home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / video / zapem-0.000 / zapem-0 / zapem / soundIt.c < prev    next >
C/C++ Source or Header  |  1995-05-28  |  13KB  |  496 lines

  1. /* SoundIt library 0.04
  2.  
  3.    Copyright 1994 Brad Pitzel  pitzel@cs.sfu.ca
  4.  
  5.    Feel free to use/distribute/modify as long as proper credits
  6.    are included.
  7. */
  8.  
  9. #include "soundIt.h"
  10. #include <malloc.h>
  11. /* #include <limits.h> */
  12. #include <stdio.h>
  13. #include <string.h>
  14. #include <unistd.h>
  15. #include <sys/types.h>
  16. #include <sys/time.h>
  17. #include <sys/stat.h>
  18. #include <fcntl.h>
  19. #include <linux/soundcard.h>
  20. #include <sys/ioctl.h>
  21. #include <sys/wait.h>
  22. #include "alopen.h"
  23.  
  24. /*==========================================================================*/
  25. /* the mix buff, where the channels are mixed into. The mix buffer is then
  26.    dumped to the sound device (/dev/dsp). Samples are mixed in
  27.    Vunclipbuf (buffer of ints), then the values in Vunclipbuf are clipped to
  28.    values between 0 and 255, and stored into Vclippedbuf (buffer of unsigned
  29.    chars).
  30. */
  31.  
  32. struct Mix
  33.     {
  34.     unsigned char *Vclippedbuf;    
  35.     int *Vunclipbuf;
  36.     int Vsize;
  37.     };
  38. typedef struct Mix Mix;
  39.  
  40. /*==========================================================================*/
  41. struct Channel
  42.     {
  43.     unsigned char *Vstart,*Vcurrent;    /* ptr's into a playing sample */
  44.     int    Vlen;                /* length of sample in bytes */
  45.     int    Vleft;                /* bytes left of sample to play */
  46.     };
  47. typedef struct Channel Channel;
  48.  
  49. /*==========================================================================*/
  50.  
  51. /* variables prefixed with S_ are static */
  52. /* 0 if mixer isn't initialized or init failed, 1 if mixer is good */
  53. static int S_mixerStatus = 0;
  54.     
  55. static const Sample     *S_sounds = NULL; /* ptr to array of samples */
  56. static int S_num_sounds = 0;        /* size of 'sounds' array above */
  57. static int S_fd_snddev = -1;        /* file # for sound device once open */
  58. static int S_fd_pipe[2] = { -1, -1 };    /* pipe to talk to child process */
  59. static int S_son_pid = -1;        /* process ID for the forked sound mixer */
  60. static const char *S_snddev = NULL;    /* char string for device, ie "/dev/dsp" */
  61. static int S_num_channels = 6;        /* number of channels to mix */
  62. static int S_playback_freq = 0;        /* playback frequency (in Hz) */
  63. /*==========================================================================*/
  64. /* non-public functions, used only within this file*/
  65.  
  66. int Snd_init_dev();
  67. int Snd_restore_dev();
  68.  
  69. void Chan_reset( Channel *chan );    /* init channel structure */
  70.  
  71.     /* start a sample playing on a channel */
  72. void Chan_assign( Channel *chan, const Sample *snd );
  73.  
  74.     /* mix all channels together into the 'mix' structure */
  75. int  Chan_mixAll( Mix *mix, Channel *ch );
  76.  
  77.     /* used by Chan_mixAll to mix the 1st channel */
  78. int  Chan_copyIn( Channel *chan, Mix *mix );
  79.  
  80.     /* used by Chan_mixAll to mix the middle channels */
  81. int  Chan_mixIn( Channel *chan, Mix *mix );
  82.  
  83.     /* used by Chan_mixAll to mix the last channel */
  84. int  Chan_finalMixIn( Channel *chan, Mix *mix );
  85.  
  86.  
  87. /* alloc mem for mix buffer, and deallocate function */
  88. /* The sound channels are mixed together into the mix buffer   */
  89. /* then the mix buffer data is sent directly to the sound device */
  90. void Mix_alloc( Mix *mix, int size );
  91. void Mix_dealloc( Mix *mix );
  92.  
  93. /*==========================================================================*/
  94. /* justing for testing, normally not called */
  95. void dump_snd_list()
  96.     {
  97.     int i=0;
  98.     
  99.     for(i=0; i<S_num_sounds; i++)
  100.         {
  101.         printf("snd %d: len = %d \n", i, S_sounds[i].len );
  102.         }
  103.     }
  104.     
  105. /*==========================================================================*/
  106. int Snd_init( int num_snd, const Sample *sa, int frequency, 
  107.               int channels, const char *dev )
  108.     {
  109.     int result;
  110.  
  111.     S_num_sounds     = num_snd;
  112.     S_sounds         = sa;    /* array of sound samples*/
  113.     S_playback_freq  = frequency;
  114.     S_num_channels   = channels; 
  115.     S_snddev= dev;    /* sound device, eg /dev/dsp*/
  116.     
  117.     if (S_sounds==NULL)
  118.         return EXIT_FAILURE;
  119.     
  120.     result=Snd_init_dev();
  121.  
  122.     if (result==EXIT_SUCCESS)
  123.         {
  124.         S_mixerStatus=1;
  125.         }
  126.     else
  127.         {
  128.         S_mixerStatus=0;
  129.         }
  130.  
  131.     return result;
  132.     }
  133.  
  134. /*==========================================================================*/
  135. int Snd_restore()
  136.     {
  137.     int result;
  138.  
  139.     if (!S_mixerStatus)
  140.         return EXIT_FAILURE;
  141.     
  142.     result=Snd_restore_dev();
  143.  
  144.     if (result==EXIT_SUCCESS)
  145.         {
  146.         S_mixerStatus=0;
  147.         }
  148.     else
  149.         {
  150.         S_mixerStatus=0;
  151.         }
  152.  
  153.     return result;
  154.     }
  155.  
  156. /*==========================================================================*/
  157. /* volume control not implemented yet.*/
  158. int Snd_effect( int sound_num, int channel )
  159.     {
  160.     if(! S_mixerStatus )
  161.         return EXIT_FAILURE;
  162.  
  163.     /* make sure a valid sound # was passed in */
  164.     if (sound_num<0 || sound_num>=S_num_sounds)
  165.         return EXIT_FAILURE;
  166.         
  167.     /* make sure a valid channel # was passed in */
  168.     if (channel<0 || channel>=S_num_channels)
  169.         return EXIT_FAILURE;
  170.         
  171.     if(S_sounds[sound_num].data != NULL)
  172.         {    
  173.         write(S_fd_pipe[1], &sound_num, sizeof(sound_num));
  174.         write(S_fd_pipe[1], &channel, sizeof(channel));
  175.         }
  176.     else
  177.         {
  178.         fprintf(stderr,"Referencing NULL sound entry\n");
  179.         return EXIT_FAILURE;
  180.         }
  181.  
  182.     return EXIT_SUCCESS;
  183.     }
  184.  
  185. /*============================================================================*/
  186. int Snd_init_dev()
  187.     {
  188.     int whoami;
  189.     S_fd_snddev = -1;
  190.  
  191.     S_son_pid = 0;
  192.  
  193.  
  194.     if(access(S_snddev,W_OK) != 0)
  195.         {    
  196.         perror("No access to sound device");
  197.         return EXIT_FAILURE;
  198.         }
  199.  
  200.     S_fd_snddev = open(S_snddev,O_WRONLY);
  201.  
  202.     if(S_fd_snddev < 0)
  203.         {    
  204.         fprintf(stderr,"int_snddev: Cannot open sound device \n");
  205.         return EXIT_FAILURE;
  206.         }
  207.         
  208.     close(S_fd_snddev);
  209.  
  210.     if(pipe(S_fd_pipe) < 0)
  211.         {    
  212.         fprintf(stderr,"Cannot create pipe for sound control \n");
  213.         return EXIT_FAILURE;
  214.         }
  215.  
  216.     /* now setup 2nd process for writing the data... */
  217.     if((whoami = fork()) < 0)
  218.         {    
  219.         fprintf(stderr,"Cannot fork sound driver\n");
  220.         return EXIT_FAILURE;
  221.         }
  222.         
  223.     if(whoami != 0)    /* successfully created son */
  224.         {    
  225.         close(S_fd_pipe[0]);    /* close end for reading */
  226.         S_son_pid = whoami;
  227.         return EXIT_SUCCESS;
  228.         }
  229.         
  230.         /* Here is the code for the son... */
  231.         {
  232.         int sound_num,ch,i;
  233.         struct timeval tval = {0L,0L};
  234.         fd_set readfds,dsp;
  235.  
  236.         Mix mix;
  237.         
  238.         int frag, fragsize;
  239.  
  240.         Channel *chan = (Channel*)malloc( sizeof(Channel)*S_num_channels );
  241.  
  242.         for (i=0; i<S_num_channels; i++)
  243.             Chan_reset( chan+i );
  244.             
  245.         S_fd_snddev = open(S_snddev,O_WRONLY );
  246.         if(S_fd_snddev < 0)
  247.             {    
  248.             perror("Cannot open sound device: ");
  249.             exit(1);
  250.             }
  251.  
  252.         frag = FRAG_SPEC; /*defined in soundIt.h */
  253.         
  254.          ioctl(S_fd_snddev, SNDCTL_DSP_SETFRAGMENT, &frag);
  255.  
  256.         if ( ioctl(S_fd_snddev,SNDCTL_DSP_SPEED, &S_playback_freq)==-1 )
  257.             perror("Sound driver ioctl ");
  258.  
  259.         fragsize=0;
  260.         if ( ioctl(S_fd_snddev,SNDCTL_DSP_GETBLKSIZE, &fragsize)==-1 ) 
  261.             perror("Sound driver ioctl ");
  262.             
  263.         /* printf("after: block size: %d \n",fragsize); */
  264.  
  265.         /* init mixer object*/
  266.         Mix_alloc( &mix, fragsize );
  267.  
  268.         close(S_fd_pipe[1]);    /* close end for writing */
  269.  
  270.         FD_ZERO(&dsp); 
  271.         FD_SET(S_fd_snddev, &dsp);
  272.         
  273.         FD_ZERO(&readfds); 
  274.         FD_SET(S_fd_pipe[0], &readfds);
  275.         
  276.         printf("soundIt library v"SOUNDIT_VERS" initialized.\n");
  277.         
  278.         for(;;)
  279.             {
  280.             FD_SET(S_fd_pipe[0], &readfds);
  281.             tval.tv_sec=0L;
  282.             tval.tv_usec=0L;
  283.             select(S_fd_pipe[0]+1, &readfds,NULL,NULL,&tval);
  284.  
  285.             if (FD_ISSET(S_fd_pipe[0], &readfds))
  286.                 {
  287.                 if (read(S_fd_pipe[0], &sound_num, sizeof(int))==0)
  288.                     break;
  289.  
  290.                 read(S_fd_pipe[0], &ch, sizeof(int));
  291.  
  292.                 /* printf("chan=%d snd=%d len=%d \n", ch, sound_num, S_sounds[sound_num].len ); */
  293.                 Chan_assign( &(chan[ch]), &(S_sounds[sound_num]) );
  294.                 }
  295.             
  296.             Chan_mixAll(&mix,chan);
  297.             write(S_fd_snddev, mix.Vclippedbuf, fragsize );
  298.             }
  299.  
  300.         Mix_dealloc( &mix );            
  301.         printf("soundIt process exiting..\n");
  302.         close(S_fd_pipe[0]);
  303.         close(S_fd_pipe[1]);
  304.         exit (0);
  305.         } /*end of child process */
  306.     }
  307.  
  308.  
  309. /*==========================================================================*/
  310. int Snd_restore_dev()
  311.     {
  312.     close(S_fd_pipe[0]);
  313.     close(S_fd_pipe[1]);
  314.     
  315.     /* wait for child process to die*/
  316.     wait(NULL);
  317.     return EXIT_SUCCESS;
  318.     }
  319.  
  320. /*==========================================================================*/
  321. /*   CHANNEL MIXING FUNCTIONS                            */
  322. /*==========================================================================*/
  323. void Chan_reset( Channel *chan )
  324.     {
  325.     chan->Vstart=NULL;
  326.     chan->Vcurrent=NULL;
  327.     chan->Vlen=0;
  328.     chan->Vleft=0;
  329.     }
  330.  
  331. /*==========================================================================*/
  332. void Chan_assign( Channel *chan, const Sample *snd )
  333.     {
  334.     chan->Vstart  = snd->data;
  335.     chan->Vcurrent= chan->Vstart;
  336.     chan->Vlen    = snd->len;
  337.     chan->Vleft   = snd->len;
  338.     } 
  339.  
  340. /*==========================================================================*/
  341. int Chan_copyIn( Channel *chan, Mix *mix )
  342.     {
  343.     int    i,*p = mix->Vunclipbuf, result, min;
  344.  
  345.     result = (chan->Vleft>0) ? 1 : 0;
  346.     min = (chan->Vleft < mix->Vsize) ? chan->Vleft : mix->Vsize;
  347.  
  348.     for(i=0; i<min; i++)
  349.         {
  350.         *p++ = (int) *chan->Vcurrent++;
  351.         }
  352.     chan->Vleft -= i;
  353.  
  354.     /* fill the remaining (if any) part of the mix buffer with silence */
  355.     while (i<mix->Vsize) 
  356.             { 
  357.             *p++ = 128; 
  358.             i++; 
  359.             }
  360.     return result;            
  361.     }
  362.  
  363. /*==========================================================================*/
  364. int Chan_mixIn( Channel *chan, Mix *mix ) 
  365.     {
  366.     int    i,*p = mix->Vunclipbuf, result, min;
  367.  
  368.     result = (chan->Vleft>0) ? 1 : 0;
  369.     min = (chan->Vleft < mix->Vsize) ? chan->Vleft : mix->Vsize;
  370.     
  371.     for(i=0; i<min; i++)
  372.         {
  373.         *p++ += (int) (*chan->Vcurrent++) - 128;
  374.         }
  375.  
  376.     chan->Vleft -= i;
  377.     return result;
  378.     }
  379.  
  380. /*========================================================================*/
  381. /* clip an int to a value between 0 and 255 */
  382. static inline 
  383. unsigned char clip(int i)
  384.     {
  385.     return (i<0) ? 0 : ( (i>255) ? 255 : i );
  386.     }
  387.     
  388. /*==========================================================================*/
  389. int Chan_finalMixIn( Channel *chan, Mix *mix )
  390.     {
  391.     register int    i;
  392.     int   *p = mix->Vunclipbuf, result, min;
  393.     unsigned char *final = mix->Vclippedbuf;
  394.  
  395.     result = (chan->Vleft>0) ? 1 : 0;
  396.     min = (chan->Vleft < mix->Vsize) ? chan->Vleft : mix->Vsize;
  397.     
  398.     for(i=0; i<min; i++)
  399.         {
  400.         *p += (int) (*chan->Vcurrent++) - 128;
  401.         *final++ = clip(*p++);
  402.         }
  403.     chan->Vleft -= i;
  404.  
  405.     /* copy rest of Vunclipbuf over to Vclippedbuf */
  406.     while (i<mix->Vsize) 
  407.             {
  408.             *final++ = clip(*p++);
  409.             i++;
  410.             }
  411.             
  412.     return result;
  413.     }
  414.  
  415.  
  416. /*==========================================================================*/
  417. void Mix_alloc(Mix *mix, int size)
  418.     {
  419.     mix->Vclippedbuf = (unsigned char *)calloc( sizeof(char), size);
  420.     mix->Vunclipbuf = (int *)calloc( sizeof(int), size);
  421.     mix->Vsize  = size;
  422.     
  423.     if ((mix->Vclippedbuf==NULL)||(mix->Vunclipbuf==NULL))
  424.         {
  425.        fprintf(stderr,"Unable to allocate memory for mixer buffer\n");
  426.         exit(-1);
  427.         }
  428.     }
  429.  
  430. /*==========================================================================*/
  431. void Mix_dealloc( Mix *mix)
  432.     { 
  433.     if (mix->Vclippedbuf) free(mix->Vclippedbuf); 
  434.     if (mix->Vunclipbuf) free(mix->Vunclipbuf); 
  435.     }
  436.  
  437. /*==========================================================================*/
  438. /* Mixes together the channels into one sound.
  439.    Returns # of channels currently playing *any* sound
  440.    Therefore, return 0 means to channels have a sample, therefore no
  441.    sound is playing
  442. */ 
  443. int Chan_mixAll( Mix *mix, Channel *chan )
  444.     {
  445.     int result  = 0,i=0;
  446.     
  447.     result  = Chan_copyIn( chan,  mix);
  448.  
  449.     /* we want to loop for S_num_channels-2 */
  450.     for(i=2;i<S_num_channels;i++)
  451.         result += Chan_mixIn( ++chan, mix);
  452.         
  453.     result += Chan_finalMixIn( ++chan, mix);
  454.     
  455.     return result;
  456.     }
  457.  
  458. /*==========================================================================*/
  459. /* given the name of a .raw sound file, load it into the Sample struct */ 
  460. /* pointed to by 'sample'                                              */
  461. /* Returns -1 couldn't open/read file                       */
  462. /*         -2 couldn't alloc memory)                                   */
  463. int
  464. Snd_loadRawSample( const char *file, Sample *sample )
  465.    {
  466.    FILE *fp;
  467.  
  468.    sample->data = NULL;
  469.    sample->len  = 0;
  470.    
  471.    fp = alopen(file,"r");   
  472.    
  473.    if (fp==NULL) return -1;
  474.    
  475.    /* get length of the file */
  476.    sample->len = lseek( fileno(fp), 0, SEEK_END );
  477.    
  478.    /* go back to beginning of file */
  479.    lseek( fileno(fp), 0, SEEK_SET );
  480.  
  481.    /* alloc memory for sample */
  482.    sample->data = (unsigned char *)malloc( sample->len );
  483.    
  484.    if (sample->data==NULL)
  485.        {
  486.        fclose(fp);
  487.        return -2;
  488.        }
  489.    
  490.    fread( sample->data, 1, sample->len, fp );     
  491.   
  492.    fclose(fp);
  493.    
  494.    return 0;
  495.    }
  496.